﻿/*  ----------------------------------------------------------------------------
 *  DES-Release 2
 *  ----------------------------------------------------------------------------
 *  File:       CollectionGateKeeper.cs
 *  Author:     nitinkoshy@gmail.com
 *  Source:     http://des.codeplex.com/
 *  ----------------------------------------------------------------------------
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Configuration;
using System.Diagnostics;
using DES.Common;

namespace DES.Server.Service.Workers
{
    public class CollectionGateKeeper : WorkerBase
    {

        public override void PerformTask(DESServerService serverService)
        {
            if (serverService.CollectionGate.Count == 0)
                return;

            Trace.Write(String.Format("[{0}/{1}]:", serverService.ServerInfo.Id, DateTime.Now.ToShortTimeString()));
            Trace.WriteLine(String.Format("CollectionGate : tasks at gate count : {0}", serverService.CollectionGate.Count));

            int waitingTaskCounter = serverService.CollectionGate.Count - 1;
            while (waitingTaskCounter >= 0)
            {
                ServerTask serverTask = serverService.CollectionGate[waitingTaskCounter];
                //Trace.Write(String.Format("[{0}/{1}]:", serverService.ServerInfo.Id, DateTime.Now.ToShortTimeString()));
                //Trace.WriteLine(String.Format("CollectionGate Message : checking task: {0}", serverTask.Task.Name));

                //figure out why the task is waiting here
                //was it waiting for its children?
                if (!serverTask.Task.ExecutionCompleteForChildren)
                {
                    //collect its children here if they too are at this gate
                    if (CollectChildTasks(serverTask, serverService))
                        waitingTaskCounter = serverService.CollectionGate.Count - 1; //reset
                    else
                        waitingTaskCounter--;
                }
                else
                    waitingTaskCounter--;


                //note:need not check for the tasks that are waiting for its parent as the parent too would be waiting at this gate
                //and the parent would collect its children in the above loop

                //reconfirm if this task can be moved back to the procesed task list
                if (!ShouldTaskWait(serverTask, serverService))
                {
                    Trace.Write(String.Format("[{0}/{1}]:", serverService.ServerInfo.Id, DateTime.Now.ToShortTimeString()));
                    Trace.WriteLine(String.Format("CollectionGate Message : Moving Task Back to Processed : {0}", serverTask.Task.Name));

                    lock (serverService.ProcessedTasks)
                    {
                        serverTask.ChildTasksCollected = true;
                        serverService.ProcessedTasks.Enqueue(serverTask);
                    }

                    lock (serverService.CollectionGate)
                    {
                        serverService.CollectionGate.Remove(serverTask);
                    }
                    ClearChildTasksFromGate(serverTask, serverService);


                    waitingTaskCounter = serverService.CollectionGate.Count - 1; //reset
                }
            }
        }

        public static void ClearChildTasksFromGate(ServerTask serverTask, DESServerService serverService)
        {
            int waitingTaskCounter = serverService.CollectionGate.Count - 1;
            while (waitingTaskCounter >= 0)
            {
                //is this a child task?
                if (serverTask.Task.Id == serverService.CollectionGate[waitingTaskCounter].Task.ParentTaskId)
                {
                    ServerTask childTask = serverService.CollectionGate[waitingTaskCounter];
                    lock (serverService.CollectionGate)
                    {
                        serverService.CollectionGate.Remove(childTask);
                        Trace.Write(String.Format("[{0}/{1}]:", serverService.ServerInfo.Id, DateTime.Now.ToShortTimeString()));
                        Trace.WriteLine(String.Format("CollectionGate : ClearChildTasksFromGate() - removing task : {0}", childTask.Task.Name));
                        DumpCollectionGate(serverService);


                    }
                    ClearChildTasksFromGate(childTask, serverService);
                    waitingTaskCounter = serverService.CollectionGate.Count - 1;//reset counter as there could a big change in count
                }
                else
                    waitingTaskCounter--;
            }
        }

        private bool CollectChildTasks(ServerTask serverTask, DESServerService serverService)
        {
            bool result = false;
            for (int childCounter = 0; childCounter < serverTask.Task.ChildTasks.Count; childCounter++)
            {
                ClientTask childTask = serverTask.Task.ChildTasks[childCounter];

                if (!childTask.ExecutionComplete)
                {
                    Trace.Write(String.Format("[{0}/{1}]:", serverService.ServerInfo.Id, DateTime.Now.ToShortTimeString()));
                    Trace.WriteLine(String.Format("CollectionGate  : CollectChildTasks() - execution not complete : {0}/{1}", serverTask.Task.Name, childTask.Name));
                    ServerTask serverTaskAtGate = GetMatchingServerTaskFromGate(childTask.Id, serverService);

                    //replace clientTask with the completed one
                    //only if the completed one itself is not waiting for another child task
                    if (serverTaskAtGate != null)
                    {
                        result = true;
                        ClientTask clientTaskAtGate = serverTaskAtGate.Task;
                        if ((clientTaskAtGate != null) && (clientTaskAtGate.ExecutionCompleteForChildren))
                        {
                            Trace.Write(String.Format("[{0}/{1}]:", serverService.ServerInfo.Id, DateTime.Now.ToShortTimeString()));
                            Trace.WriteLine(String.Format("CollectionGate : Got matching completed task for : {0}/{1}", serverTask.Task.Name, clientTaskAtGate.Name));
                            serverTask.Task.ChildTasks[childCounter] = clientTaskAtGate; //replace with completed task

                            lock (serverService.CollectionGate)
                            {
                                ClearChildTasksFromGate(serverTaskAtGate, serverService);//clear all child
                                serverService.CollectionGate.Remove(serverTaskAtGate);
                                Trace.Write(String.Format("[{0}/{1}]:", serverService.ServerInfo.Id, DateTime.Now.ToShortTimeString()));
                                Trace.WriteLine(String.Format("CollectionGate : CollectChildTasks() - removing task : {0}", serverTaskAtGate.Task.Name));
                                DumpCollectionGate(serverService);
                            }
                        }


                    }
                }
            }
            return result;
        }

        private ServerTask GetMatchingServerTaskFromGate(Guid taskGuid, DESServerService serverService)
        {
            for (int waitingTaskCount = 0; waitingTaskCount < serverService.CollectionGate.Count; waitingTaskCount++)
            {
                ServerTask serverTask = serverService.CollectionGate[waitingTaskCount];
                if (serverTask.Task.Id == taskGuid)
                {
                    return serverTask;
                }
            }

            return null;
        }

        public override string GetWorkerName()
        {
            return "CollectionGateKeeper";
        }

        public override int GetThreadSleepTime()
        {
            return Convert.ToInt32(ConfigurationManager.AppSettings["taskgathererthreadsleeptime"]);
        }

        public static bool ShouldTaskWait(ServerTask serverTask, DESServerService serverService)
        {
            //there are child tasks that needs to complete
            //and the task has reached the server where the child tasks were considered for execution
            if (!serverTask.Task.ExecutionCompleteForChildren)
                if (serverTask.ChildTasksMarkedAtServer == null)
                    return true;
                else
                    return (serverTask.HasReachedWaitingServer(serverService));

            //though the child tasks are complete and have reached the originating server,
            //the parent task itself is not around.
            if (serverTask.Task.ExecutionCompleteForChildren)
                if (serverTask.Task.ParentTaskId.HasValue)
                    if (serverTask.HasReachedRoot(serverService)) //the root for the child task would be the server at which it got distributed.
                        return true;

            return false;
        }

        public static void DumpCollectionGate(DESServerService serverService)
        {
            string taskNames = String.Empty;
            foreach (ServerTask serverTask in serverService.CollectionGate)
                taskNames += "/" + serverTask.Task.Name;
            Trace.Write(String.Format("[{0}/{1}]:", serverService.ServerInfo.Id, DateTime.Now.ToShortTimeString()));
            Trace.WriteLine("Collection Gate Dump {0}", taskNames);
        }
    }
}
